package com.benjaminwan.read.free.net.service

import android.text.TextUtils
import com.benjaminwan.read.free.R
import com.benjaminwan.read.free.app.App
import com.benjaminwan.read.free.model.bean.ContentAttach
import com.benjaminwan.read.free.model.bean.ContentAttachInfo
import com.benjaminwan.read.free.model.bean.PrePostInfoBean
import com.benjaminwan.read.free.model.enums.NetRetState
import com.benjaminwan.read.free.model.result.*
import com.benjaminwan.read.free.net.RetrofitFactory
import com.benjaminwan.read.free.net.api.HiPDAApi
import com.benjaminwan.read.free.ui.net.NetViewModel
import com.benjaminwan.read.free.utils.HiPDAPassUtils.processedPassword
import com.benjaminwan.read.free.utils.HiParserThreadDetail
import com.benjaminwan.read.free.utils.HiUtils.getMiddleString
import com.benjaminwan.read.free.utils.getAppVersionName
import com.benjaminwan.read.free.utils.getString
import com.orhanobut.logger.Logger
import io.reactivex.Observable
import okhttp3.ResponseBody
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.select.Elements
import java.util.*

object HiPDAService {
    fun getFormHash(): Observable<NetResult> {
        return RetrofitFactory.create(HiPDAApi::class.java)
            .getFormHash()
            .convertHash()
    }

    fun postLogin(
        formHash: String, username: String,
        password: String, questionId: String,
        answer: String
    ): Observable<LoginResult> {
        //Logger.i("name=${URLEncoder.encode(username,"GBK")},answer=${URLEncoder.encode(answer,"GBK")}")
        return RetrofitFactory.create(HiPDAApi::class.java)
            .postLogin(
                formHash = formHash, username = username,
                password = processedPassword(password), questionId = questionId,
                answer = answer
            ).convertLogin()
    }

    fun getForum(fid: String, page: String): Observable<String> {
        return RetrofitFactory.create(HiPDAApi::class.java)
            .getForumDisplay(fid, page)
            .convertStr()
    }

    fun getPM(): Observable<String> {
        return RetrofitFactory.create(HiPDAApi::class.java)
            .getPM()
            .convertStr()
    }

    fun getThread(tid: String, page: Int): Observable<ThreadResult> {
        return RetrofitFactory.create(HiPDAApi::class.java)
            .getThread(tid, page.toString())
            .convertThread()
    }

    fun getVersion(): Observable<VersionResult> {
        return RetrofitFactory.create(HiPDAApi::class.java)
            .getThread(NetViewModel.DefaultTid, 1.toString())
            .convertVersion()
    }

    fun getCDN(): Observable<String> {
        return RetrofitFactory.create(HiPDAApi::class.java)
            .getCDN()
            .convertStr()
    }

    fun getPrePost(fid: String): Observable<PrePostResult> {
        return RetrofitFactory.create(HiPDAApi::class.java)
            .getPrePost(fid)
            .convertPrePost()
    }

    fun postReply(
        tid: String, formhash: String, message: String
    ): Observable<String> {
        return RetrofitFactory.create(HiPDAApi::class.java)
            .postReply(tid = tid, formhash = formhash, message = message)
            .convertStr()
    }

    private fun Observable<ResponseBody>.convertStr(): Observable<String> =
        this.flatMap {
            val xmlStr = it.string()
            //Logger.i(xmlStr)
            val result: Observable<String> = if (xmlStr.isEmpty()) {
                Observable.error(Throwable("服务端返回空数据"))
            } else {
                Observable.just(xmlStr)
            }
            result
        }

    private fun Observable<ResponseBody>.convertHash(): Observable<NetResult> =
        this.flatMap {
            val xmlStr = it.string()
            //Logger.i(xmlStr)
            val result: Observable<NetResult> = if (xmlStr.isEmpty()) {
                Observable.error(Throwable("服务端返回空数据"))
            } else {
                val ret = getFormHash(xmlStr)
                Observable.just(ret)
            }
            result
        }

    private fun Observable<ResponseBody>.convertLogin(): Observable<LoginResult> =
        this.flatMap {
            val xmlStr = it.string()
            val result: Observable<LoginResult> = if (xmlStr.isEmpty()) {
                Observable.error(Throwable("服务端返回空数据"))
            } else {
                val ret = getLoginResult(xmlStr)
                RetrofitFactory.saveLoginResult(ret)
                Observable.just(ret)
            }
            result
        }

    private fun Observable<ResponseBody>.convertThread(): Observable<ThreadResult> =
        this.flatMap {
            val xmlStr = it.string()
            val threadResult = getThreadResult(xmlStr)
            val result: Observable<ThreadResult> =
                if (threadResult.state == NetRetState.SUCCESS || threadResult.state == NetRetState.RE_LOGIN) {
                    Observable.just(threadResult)
                } else {
                    Observable.error(Throwable(threadResult.msg))
                }
            result
        }

    private fun Observable<ResponseBody>.convertVersion(): Observable<VersionResult> =
        this.flatMap {
            val xmlStr = it.string()
            val versionResult = getVersionResult(xmlStr)
            val result: Observable<VersionResult> =
                if (versionResult.state == NetRetState.SUCCESS || versionResult.state == NetRetState.RE_LOGIN) {
                    Observable.just(versionResult)
                } else {
                    Observable.error(Throwable(versionResult.msg))
                }
            result
        }

    private fun Observable<ResponseBody>.convertPrePost(): Observable<PrePostResult> =
        this.flatMap {
            val xmlStr = it.string()
            val prePostResult = getPrePostResult(xmlStr)
            val result: Observable<PrePostResult> =
                if (prePostResult.state == NetRetState.SUCCESS || prePostResult.state == NetRetState.RE_LOGIN) {
                    Observable.just(prePostResult)
                } else {
                    Observable.error(Throwable(prePostResult.msg))
                }
            result
        }

    private fun getFormHash(ret: String): NetResult {
        if (ret.isNotEmpty()) {
            try {
                val doc: Document = Jsoup.parse(ret)

                val elements = doc.select("input[name=formhash]")
                val element = elements.first()

                if (element == null) {
                    val alartES: Elements = doc.select("div.alert_info")
                    val errMsg = if (alartES.size > 0) {
                        alartES.first().text()
                    } else {
                        "Can NOT get formhash"
                    }
                    return NetResult(
                        NetRetState.FAIL, errMsg
                    )
                }
                return NetResult(
                    NetRetState.SUCCESS, element.attr("value")
                )
            } catch (e: Exception) {
                Logger.e(e.message.toString())
            }
        }
        return NetResult(
            NetRetState.FAIL, "服务器返回空数据"
        )
    }

    private fun getLoginResult(rspStr: String): LoginResult {
        // response is in XML format
        return when {
            rspStr.contains(getString(R.string.login_success)) -> {
                if (rspStr.contains("您的帐号处于非激活状态") || rspStr.contains("待验证的群众")) {
                    LoginResult(
                        NetRetState.FAIL_ABORT, getString(R.string.login_inactive)
                    )
                } else {
                    LoginResult(
                        NetRetState.SUCCESS, "登陆成功!"
                    )
                }

            }
            rspStr.contains(getString(R.string.login_fail)) -> {
                Logger.e("Login FAIL")
                val msgIndex: Int = rspStr.indexOf(getString(R.string.login_fail))
                val msgIndexEnd: Int = rspStr.indexOf("次", msgIndex) + 1
                val errMsg = if (msgIndexEnd > msgIndex) {
                    rspStr.substring(msgIndex, msgIndexEnd)
                } else {
                    "登录失败,请检查账户信息"
                }
                LoginResult(
                    NetRetState.FAIL_ABORT, errMsg
                )
            }
            else -> {
                LoginResult(
                    NetRetState.FAIL_ABORT, "登录失败,未知错误"
                )
            }
        }
    }

    private fun getThreadResult(rspStr: String): ThreadResult {
        return if (rspStr.isEmpty()) {
            ThreadResult(
                NetRetState.FAIL, "服务端返回空数据", null
            )
        } else if (!checkLoggedin(rspStr)) {
            ThreadResult(
                NetRetState.RE_LOGIN, "登陆信息失效，需要重新登陆!", null
            )
        } else {
            val doc = Jsoup.parse(rspStr)
            val tid: String = getMiddleString(rspStr, "tid = parseInt('", "')")
            val detailList = HiParserThreadDetail.parse(doc, tid)
            ThreadResult(
                NetRetState.SUCCESS, "获取信息成功", detailList
            )
        }
    }

    private fun getVersionResult(rspStr: String): VersionResult {
        return if (rspStr.isEmpty()) {
            VersionResult(
                NetRetState.FAIL, "服务端返回空数据", false, "", ""
            )
        } else if (!checkLoggedin(rspStr)) {
            VersionResult(
                NetRetState.RE_LOGIN, "登陆信息失效，需要重新登陆!", false, "", ""
            )
        } else {
            val doc = Jsoup.parse(rspStr)
            val tid: String = getMiddleString(rspStr, "tid = parseInt('", "')")
            val detailList = HiParserThreadDetail.parse(doc, tid)
            val firstPost = detailList.all.firstOrNull()
            val lastAttach =
                firstPost?.contents?.list?.filterIsInstance<ContentAttach>()?.lastOrNull()
            val lastAttachInfo =
                firstPost?.contents?.list?.filterIsInstance<ContentAttachInfo>()?.lastOrNull()?.content ?: ""
            if (lastAttach == null) {
                VersionResult(
                    NetRetState.FAIL, "无法获取更新信息", false, "", ""
                )
            } else {
                Logger.i("$lastAttach")
                val titleSplit = lastAttach.mTitle.split("-")
                if (titleSplit.size != 3 || lastAttach.mUrl.isEmpty()) {
                    VersionResult(
                        NetRetState.FAIL, "无法获取更新信息", false, "", ""
                    )
                } else {
                    val remoteVersion = titleSplit[1]
                    val appVersion = App.INSTANCE.getAppVersionName()
                    val maxVersion = maxOf(remoteVersion, appVersion)
                    val isNeedUpdate = maxVersion != appVersion
                    VersionResult(
                        NetRetState.SUCCESS,
                        "获取更新信息成功",
                        isNeedUpdate,
                        remoteVersion,
                        lastAttach.mUrl,
                        lastAttachInfo
                    )
                }
            }
        }
    }

    private fun getPrePostResult(rspStr: String): PrePostResult {
        return if (rspStr.isEmpty()) {
            PrePostResult(
                NetRetState.FAIL, "服务端返回空数据", null
            )
        } else if (!checkLoggedin(rspStr)) {
            PrePostResult(
                NetRetState.RE_LOGIN, "登陆信息失效，需要重新登陆!", null
            )
        } else {
            val doc = Jsoup.parse(rspStr)
            val prePostInfo = parseRsp(doc)
            val msg = if (prePostInfo == null) "页面解析错误" else "获取信息成功"
            val state = if (prePostInfo == null) NetRetState.FAIL else NetRetState.SUCCESS
            PrePostResult(state, msg, prePostInfo)
        }
    }

    private fun parseRsp(doc: Document): PrePostInfoBean? {
        val prePostInfo = PrePostInfoBean()
        val formhashES = doc.select("input[name=formhash]")
        if (formhashES.size < 1) {
            return null
        } else {
            prePostInfo.formhash = formhashES.first().attr("value")
        }

        val scriptES = doc.select("script")
        if (scriptES.size < 1) {
            return prePostInfo
        } else {
            prePostInfo.uid = getMiddleString(scriptES.first().data(), "discuz_uid = ", ",")
        }
        val hashES = doc.select("input[name=hash]")
        if (hashES.size < 1) {
            return prePostInfo
        } else {
            prePostInfo.hash = hashES.first().attr("value")
        }

        //for edit post
        val subjectES = doc.select("input[name=subject]")
        if (subjectES.size > 0) {
            prePostInfo.subject = subjectES.first().attr("value")
        }
        val deleteCheckBox = doc.select("input#delete")
        if (deleteCheckBox.size > 0) {
            prePostInfo.isDeleteable = true
        }
        val uploadInfoES = doc.select("div.uploadinfo")
        if (uploadInfoES.size > 0) {
            val uploadInfo = uploadInfoES.first().text()
            if (uploadInfo.contains("文件尺寸")) {
                val sizeText: String = getMiddleString(uploadInfo.toUpperCase(), "小于", "B").trim()
                //sizeText : 100KB 8MB
                /*try {
                    val size = sizeText.substring(0, sizeText.length - 1).toFloat()
                    val unit = sizeText.substring(sizeText.length - 1)
                    if (size > 0) {
                        var maxFileSize = 0
                        if ("K" == unit) {
                            maxFileSize = (size * 1024).toInt()
                        } else if ("M" == unit) {
                            maxFileSize = (size * 1024 * 1024).toInt()
                        }
                        if (maxFileSize > 1024) HiSettingsHelper.getInstance()
                            .setMaxUploadFileSize(maxFileSize)
                    }
                } catch (ignored: java.lang.Exception) {
                }*/
            }
        }


        val unusedImagesES =
            doc.select("div#unusedimgattachlist table.imglist img")
        for (i in unusedImagesES.indices) {
            val imgE = unusedImagesES[i]
            val href: String = imgE.attr("src").toString()
            var imgId: String = imgE.attr("id").toString()
            if (href.contains("attachments/") && imgId.contains("_")) {
                imgId = imgId.substring(imgId.lastIndexOf("_") + 1)
                if (imgId.isNotEmpty() && TextUtils.isDigitsOnly(imgId)) {
                    prePostInfo.allImages.add(imgId)
                }
            }
        }

        //uploaded image list
        val uploadedImagesES =
            doc.select("div.upfilelist img[id^=image_]")
        for (i in uploadedImagesES.indices) {
            val imgE = uploadedImagesES[i]
            var imgId: String = imgE.attr("id").toString()
            imgId = imgId.substring("image_".length)
            if (imgId.isNotEmpty() && TextUtils.isDigitsOnly(imgId)) {
                prePostInfo.allImages.add(imgId)
            }
        }

        //image as attachments
        val attachmentImages = doc.select("div.upfilelist span a")
        for (i in attachmentImages.indices) {
            val aTag = attachmentImages[i]
            val href: String = aTag.attr("href").toString()
            val onclick: String = aTag.attr("onclick").toString()
            if (href.startsWith("javascript")) {
                if (onclick.startsWith("insertAttachimgTag")) {
                    //<a href="javascript:;" class="lighttxt" onclick="insertAttachimgTag('2810014')" title="...">Hi_160723_2240.jpg</a>
                    val imgId: String = getMiddleString(onclick, "insertAttachimgTag('", "'")
                    if (!TextUtils.isEmpty(imgId) && TextUtils.isDigitsOnly(imgId)) {
                        prePostInfo.allImages.add(imgId)
                    }
                } else if (onclick.startsWith("insertAttachTag")) {
                    val attachId: String = getMiddleString(onclick, "insertAttachTag('", "'")
                    if (!TextUtils.isEmpty(attachId) && TextUtils.isDigitsOnly(attachId)) {
                        prePostInfo.attaches.add(attachId)
                    }
                }
            }
        }
        val typeidES = doc.select("#typeid > option")
        val values: MutableMap<String, String> =
            LinkedHashMap()
        for (i in typeidES.indices) {
            val typeidEl = typeidES[i]
            values[typeidEl.`val`()] = typeidEl.text()
            if (i == 0 || "selected" == typeidEl.attr("selected")) prePostInfo.typeId =
                typeidEl.`val`()
        }
        prePostInfo.typeValues.putAll(values)
        return prePostInfo
    }

    fun checkLoggedin(mRsp: String): Boolean {
        val loggedIn = !mRsp.contains(getString(R.string.not_login))
        //if (!loggedIn) RetrofitFactory.clearCookies()
        return loggedIn
    }

}